#include <string.h>
#include "threads/vaddr.h"
#include "threads/synch.h"
#include "threads/malloc.h"
#include "devices/disk.h"
#include "vm/frame.h"
#include "vm/swap.h"
#include "vm/page.h"

void swap_init ()
{
   swap_disk = disk_get (1, 1);
   swap_max_page_num = disk_size (swap_disk) * DISK_SECTOR_SIZE / PGSIZE;

   // printf("swap_max_page_num: %d, PGSIZE: %d, DISK_SECTOR_SIZE: %d\n", swap_max_page_num, PGSIZE, DISK_SECTOR_SIZE);

   swap_info_next_idx = 0;
   swap_list = (struct swap_info*) malloc (sizeof(struct swap_info) * swap_max_page_num);
   uint32_t i;

   for (i = 0; i < swap_max_page_num; i++)
   {
       swap_list[i].status = -1;
   }
}

uint32_t get_swapped_page (struct page_id* arg_id)
{
   uint32_t i;

   for (i = 0; i < swap_max_page_num; i++)
   {
       if (swap_list[i].status > -1 && 
           swap_list[i].id.owner_t == arg_id->owner_t && swap_list[i].id.upage == arg_id->upage)
       {
          return i;
       }
   }
   return swap_max_page_num;
}

void swap_load (struct page_id *arg_id, void* kpage)
{
   uint32_t i = get_swapped_page (arg_id);
   uint32_t j;

   for (j = 0; j < PGSIZE / DISK_SECTOR_SIZE; j++)
   {
       disk_read (swap_disk, i * PGSIZE / DISK_SECTOR_SIZE + j, kpage + j * DISK_SECTOR_SIZE);
   }
   swap_list[i].status = 1;
}

void swap_out (uint32_t arg_i, void* kpage)
{
   uint32_t j;
   for (j = 0; j < PGSIZE / DISK_SECTOR_SIZE; j++)
   {
       disk_write (swap_disk, arg_i * PGSIZE / DISK_SECTOR_SIZE + j, kpage + j * DISK_SECTOR_SIZE);
   }
}

void swap_release (struct thread *arg_t)
{
   uint32_t i;

   for (i = 0; i < swap_max_page_num; i++)
   {
       if (swap_list[i].status > -1 
          && swap_list[i].id.owner_t == arg_t)
       {
          swap_list[i].status = -1;
       }
   }
}

bool swap_allocate (struct page_id* arg_id, struct page_id* old_arg_id, uint32_t* arg_index)
{
   if (swap_max_page_num == 0)
   {
      return false;
   }

   uint32_t i = get_swapped_page (arg_id);

   if (i != swap_max_page_num)
   {
      memcpy (old_arg_id, &swap_list[i].id, sizeof(*old_arg_id));
      *arg_index = i;
      swap_list[i].status = 0;

      return true;
   }

   bool check = false;

   while (swap_list[swap_info_next_idx].status == 0)
   {
      if (check)
      {
         return false;
      }
      swap_info_next_idx = (swap_info_next_idx + 1) % swap_max_page_num;
      check = true;
   }

   i = swap_info_next_idx;

   if (swap_list[i].status > -1)
   {
      memcpy (old_arg_id, &swap_list[i].id, sizeof (*old_arg_id));
   }
   else
   {
      old_arg_id->owner_t = NULL;
      old_arg_id->upage = NULL;
   }

   memcpy (&swap_list[i].id, arg_id, sizeof (swap_list[i].id));
   *arg_index = i;
   swap_list[i].status = 0;

   return true;
}
